/*
|			Tablebase file operations
\*---------------------------------------------------------------------*/

#include "fileoper.h"
#include "cache.h"
#include "gtb-dec.h"
#include "debug.h"
#include "compression.h"

namespace Tablebases
{

// endgame directory
char szEndGamePath[MAX_PATH];

// buffer for packing and unpacking
static unsigned char 	Buffer_zipped [EGTB_MAXBLOCKSIZE];
static unsigned char 	Buffer_packed [EGTB_MAXBLOCKSIZE];

/* LOCKS */
#ifdef THREADING
	extern mythread_mutex_t	Egtb_lock;
#endif


 // Path Management
/////////////////////////////////////////////////////////////////////

extern void tb_SetEndGamePath(const char* szPath)
{
	strcpy_s(szEndGamePath, MAX_PATH, szPath);
}

extern char* tb_GetEndGamePath()
{
	return szEndGamePath;
}

 // End game keys
//////////////////////////////////////////////////////////////////////

mySHARED struct endgamekey egkey[] = {
	{0, "db2",  NULL, 0 },
	{0, "db3",  NULL, 0 },
	{0, "db4",  NULL, 0 },
	{0, "db5",  NULL, 0 },
	{0, "db6",  NULL, 0 },
	{0, "db7",  NULL, 0 },
	{0, "db8",  NULL, 0 }
};

static int eg_was_open[MAX_EGKEYS];

struct subendgame egSubDb[MAX_WM][MAX_WK][MAX_BM][MAX_BK][MAX_WMRANK][MAX_BMRANK][MAX_COLOR];


  // Zip info init
//////////////////////////////////////////////////////////////////////


// initializes databases and loads indexes
extern unsigned int
zipinfo_init (void)
{
	int i, start, end;
	unsigned ret;
	bool_t ok;
	int j;
	int max = 0;

	/* reset all values */
	memset(egSubDb, 0, sizeof(subendgame) * MAX_WM  * MAX_WK * MAX_BM * MAX_BK * MAX_WMRANK * MAX_BMRANK * MAX_COLOR);

	/* load all values */
	start = 0;
	end   = MAX_EGKEYS;
	for (i = start, ok = TRUE; i < end; i++) {
		ok = NULL != fd_openit(i);
		if( ! ok)
		{
			max = i + 2;
			break;
		}

		ok = ok && egtb_loadindexes (i);
	}

	return max;
}

// frees index memory
extern void
zipinfo_done (void)
{
	int wm = 0, wk = 0, bm = 0, bk = 0, wmrank = 0, bmrank = 0, color = 0;
	subendgame* p = NULL;

	for(wm = 0; wm < MAX_WM; wm++)
		for(wk = 0; wk < MAX_WK; wk++)
			for(bm = 0; bm < MAX_BM; bm++)
				for(bk = 0; bk < MAX_BK; bk++)
					for(wmrank = 0; wmrank < MAX_WMRANK; wmrank++)
						for(bmrank = 0; bmrank < MAX_BMRANK; bmrank++)
							for(color = 0; color < MAX_COLOR; color++)
							{
								p = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);
								if (p->zipInfo.blockindex != NULL) {
									free(p->zipInfo.blockindex);
									p->zipInfo.blockindex = NULL;
		 							p->zipInfo.extraoffset = 0;
		 							p->zipInfo.totalblocks = 0;
								}
							}
	return;
}

// returns memory allocated by indexes
static size_t
zipinfo_memory_allocated (void)
{
	int wm = 0, wk = 0, bm = 0, bk = 0, wmrank = 0, bmrank = 0, color = 0;
	subendgame* p = NULL;
	index_t accum_blocks = 0;

	for(wm = 0; wm < MAX_WM; wm++)
		for(wk = 0; wk < MAX_WK; wk++)
			for(bm = 0; bm < MAX_BM; bm++)
				for(bk = 0; bk < MAX_BK; bk++)
					for(wmrank = 0; wmrank < MAX_WMRANK; wmrank++)
						for(bmrank = 0; bmrank < MAX_BMRANK; bmrank++)
							for(color = 0; color < MAX_COLOR; color++)
							{
								p = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);
								if (p->zipInfo.blockindex != NULL) 
								{
			 						accum_blocks += p->zipInfo.totalblocks;
								}
							}

	return (size_t)accum_blocks * sizeof(index_t);
}

// wrapper, returns memory allocated by indexes
extern size_t
tb_indexmemory (void)
{
	return zipinfo_memory_allocated ();
}

 // File operations
/////////////////////////////////////////////////////////////////////


// Opens specific database file
mySHARED /*@NULL@*/ FILE *
fd_openit (tbkey_t key)
{	
	int 			i;
	FILE *			finp = NULL;
	char	 		buf[4096];
	char *			filename = buf;
	const char *	extension;

	assert (0 <= key && key < MAX_EGKEYS);
	assert (0 <= fd.n && fd.n <= GTB_MAXOPEN);

	/* set proper extensions to the File */
	if (Uncompressed) {
		assert (decoding_scheme() == 0 && GTB_scheme == 0);
		extension = ".cpr";
	} else {
		extension = Extension[decoding_scheme()];
	}

	sprintf_s(buf, 4096, "%s%s%s", szEndGamePath, egkey[key].str, extension);

	finp = fopen (filename, "rb");
	/* File was found and opened */
	if (NULL != finp) {
		egkey[key].fd = finp;
		eg_was_open[key] = 1;
		return finp;
	}

	return finp;
}


// reads unsigned long number
static bool_t
fread32 (FILE *f, /*@out@*/ unsigned long int *y)
{
	enum SIZE {SZ = 4};
	int i;
	unsigned long int x;
	unsigned char p[SZ];
	bool_t ok;

	ok = (SZ == fread (p, sizeof(unsigned char), SZ, f));

	if (ok) {
		for (x = 0, i = 0; i < SZ; i++) {
			x |= (unsigned long int)p[i] << (i*8);
		}
		*y = x;
	}
	return ok;
}

// loads all subdatabases and all its indexes
static bool_t
egtb_loadindexes (tbkey_t key)
{

	unsigned long int i;
	unsigned long int idx = 0; /* to silence warning "may be used uninitialized..." */
	unsigned long int nSubDbCount = 0;
	unsigned long int nSubDbIndex = 0;
	struct subdbRec rec;
	subendgame *pSubEndGame = NULL;
	index_t	*p;
	bool_t ok;
	FILE *f;

	if (Uncompressed) {
		assert (decoding_scheme() == 0 && GTB_scheme == 0);	
		return TRUE; /* no need to load indexes */
	}

	if (NULL == (f = egkey[key].fd))
		return FALSE; /* file was no open */

	// get subdb count
	ok = (0 == fseek (f, 0, SEEK_SET))
		&& fread32 (f, &nSubDbCount);

	// iterate through all subdb
	for(i = 0; i < nSubDbCount; i++)
	{
		// set dub db info
		ok  = ok && fread(&rec, 1, sizeof(rec), f);
		pSubEndGame = &(egSubDb[rec.wm][rec.wk][rec.bm][rec.bk][rec.wmrank][rec.bmrank][rec.color]);
		pSubEndGame->db = key;
		pSubEndGame->offset = rec.offset;
		pSubEndGame->zipInfo.blockindex = (index_t*)malloc( (rec.numblocks + 1) * sizeof(index_t));
		pSubEndGame->zipInfo.totalblocks = rec.numblocks;
		pSubEndGame->zipInfo.extraoffset = 0;
		pSubEndGame->maxindex = rec.maxindex;

		// load indexes
		p = pSubEndGame->zipInfo.blockindex;
		for (i = 0; ok && i < pSubEndGame->zipInfo.totalblocks; i++) {
			ok = fread32 (f, &idx); /* idx will be used if ok is true, otherwise, it is discarded */
			p[i] = (index_t)idx; /* reads a 32 bit int, and converts it to index_t */ assert (sizeof(index_t) >= 4);
		} // for

		p[rec.numblocks] = rec.maxindex;

		if(!ok )
		{
			free(p);
			pSubEndGame->zipInfo.blockindex = NULL;
			break;
		}
	}

	return ok;
}

// support for uncompressed databases possibly later
//static index_t
//egtb_block_uncompressed_to_index (tbkey_t key, index_t b)
//{
//	index_t max;
//	index_t blocks_per_side;
//	index_t idx;
//
//	max = egkey[key].maxindex;
//	blocks_per_side = 1 + (max-1) / (index_t)dtm_cache.entries_per_block;
//
//	if (b < blocks_per_side) {
//		idx = 0;
//	} else {
//		b -= blocks_per_side;
//		idx = max;
//	}
//	idx += b * (index_t)dtm_cache.entries_per_block;
//	return idx;
//}


// returns the block number by given subdatabase and index in it
static index_t
egtb_block_getnumber (int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t idx)
{
	index_t block_in_side;
	subendgame* p = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);
	
	assert(p->zipInfo.blockindex != NULL);

	block_in_side   = idx / (index_t)dtm_cache.entries_per_block;

	assert(block_in_side <= p->zipInfo.totalblocks);

	return block_in_side; /* block */
}


// returns the block size at give index
static index_t 
egtb_block_getsize (int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t idx)
{
	index_t blocksz = (index_t) dtm_cache.entries_per_block;
	subendgame* p = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);
	index_t maxindex  = p->maxindex;
	index_t block, offset, x; 

	assert (dtm_cache.entries_per_block <= MAXINDEX_T);
	assert (0 <= idx && idx < maxindex);
	assert (key < MAX_EGKEYS);

	block = idx / blocksz;
	offset = block * blocksz;

	/* 
	|	adjust block size in case that this is the last block 
	|	and is shorter than "blocksz" 
	*/
	if ( (offset + blocksz) > maxindex) 
		x = maxindex - offset; /* last block size */
	else
		x = blocksz; /* size of a normal block */
	
	return x;
}

// returns the block size in compressed state
static index_t 
egtb_block_getsize_zipped (int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t block )
{
	index_t i, j;
	subendgame* p = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);
	assert (p->zipInfo.blockindex != NULL);
	i = p->zipInfo.blockindex[block];
	j = p->zipInfo.blockindex[block+1];	
	return j - i;
}

// makes the file ready open
static bool_t
egtb_file_beready (tbkey_t key)
{
	bool_t success;
	assert (key < MAX_EGKEYS);
	success = 	(NULL != egkey[key].fd) ||
				(NULL != fd_openit(key) && egtb_loadindexes (key));
	return success; 
}


// seek the file to the given block
static bool_t
egtb_block_park  (tbkey_t key, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t block)
{
	index_t i;
	long fseek_i;
	assert (egkey[key].fd != NULL);
	subendgame* p = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);


	// support for uncompressed db later
	//if (Uncompressed) {
	//	assert (decoding_scheme() == 0 && GTB_scheme == 0);	
	//	i = egtb_block_uncompressed_to_index (key, block);
	//} else {
	assert (p->zipInfo.blockindex != NULL);
		i  = p->zipInfo.blockindex[block];
		i += p->zipInfo.extraoffset;
		i += p->offset;
	//}

	fseek_i = (long) i;
	assert (fseek_i >= 0);
	return 0 == fseek (egkey[key].fd, fseek_i, SEEK_SET);
}


// reads the given block from file
static bool_t
egtb_block_read (tbkey_t key, index_t len, unsigned char *buffer) 
{
	assert (egkey[key].fd != NULL);
	assert (sizeof(size_t) >= sizeof(len));
	return ((size_t)len == fread (buffer, sizeof (unsigned char), (size_t)len, egkey[key].fd));	
}

tbkey_t TB_PROBE_indexing_dummy;

// decompresses given block with given compression
static bool_t
egtb_block_decode (tbkey_t key, index_t z, unsigned char *bz, index_t n, unsigned char *bp)
/* bz:buffer zipped to bp:buffer packed */
{
	size_t zz = (size_t) z;
	size_t nn = (size_t) n;
	TB_PROBE_indexing_dummy = key; /* to silence compiler */
	assert (sizeof(size_t) >= sizeof(n));
	assert (sizeof(size_t) >= sizeof(z));
	return decode (zz-1, bz+1, nn, bp);
}

// there is doubt, this style of packing plies is needed for checkers
// *******************************************************************
//mySHARED dtm_t
//dtm_unpack (unsigned stm, unsigned char packed)
//{
//	unsigned int info, plies, prefx, store, moves;
//	dtm_t ret;
//	unsigned int p = packed;
//
//	if (iDRAW == p || iFORBID == p) {
//		return (dtm_t) p;
//	}
//	
//	info  = (unsigned int) p & 3;
//	store = (unsigned int) p >> 2;
//
//	if (WH == stm) {
//		switch (info) {
//			case iWMATE:		
//						moves = store + 1;
//						plies = moves * 2 - 1;
//						prefx = info;
//						break;				
//
//			case iBMATE:
//						moves = store;
//						plies = moves * 2;
//						prefx = info;
//						break;
//
//			case iDRAW:
//						moves = store + 1 + 63;
//						plies = moves * 2 - 1;
//						prefx = iWMATE;						
//						break;
//
//			case iFORBID:
//
//						moves = store + 63;
//						plies = moves * 2;
//						prefx = iBMATE;
//						break;
//			default:	
//            plies = 0;
//            prefx = 0;
//            assert(0);
//						break;
//
//		}
//		ret = (dtm_t) (prefx | (plies << 3));
//	} else {
//		switch (info) {
//			case iBMATE:		
//						moves = store + 1;
//						plies = moves * 2 - 1;
//						prefx = info;
//						break;				
//
//			case iWMATE:
//						moves = store;
//						plies = moves * 2;
//						prefx = info;
//						break;
//
//			case iDRAW:
//
//						if (store == 63) {
//						/* 	exception: no position in the 5-man 
//							TBs needs to store 63 for iBMATE 
//							it is then used to indicate iWMATE 
//							when just overflows */
//							store++;
//
//							moves = store + 63;
//							plies = moves * 2;
//							prefx = iWMATE;	
//
//							break;
//						}
//	
//						moves = store + 1 + 63;
//						plies = moves * 2 - 1;
//						prefx = iBMATE;						
//						break;
//
//			case iFORBID:
//
//						moves = store + 63;
//						plies = moves * 2;
//						prefx = iWMATE;
//						break;
//			default:	
//            plies = 0;
//            prefx = 0;
//            assert(0);
//						break;
//
//		}
//		ret = (dtm_t) (prefx | (plies << 3));
//	}
//	return ret;	
//}

// there is doubt, this style of packing plies is needed for checkers
// *******************************************************************
//static bool_t
//egtb_block_unpack (unsigned side, index_t n, const unsigned char *bp, dtm_t *out)
///* bp:buffer packed to out:distance to mate buffer */
//{
//	index_t i;
//	for (i = 0; i < n; i++) {
//		*out++ = dtm_unpack (side, bp[i]);		
//	}	
//	return TRUE;
//} 


// loads DTM block to DTM cache
static bool_t
preload_cache (tbkey_t key, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t idx)
/* output to the least used block of the cache */
{
	dtm_block_t 	*pblock;
	dtm_t 			*p;
	bool_t 			ok;

	FOLLOW_label("preload_cache starts")

	struct subendgame* pSubDb = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);
	if (idx >= pSubDb->maxindex) {
		FOLLOW_LULU("Wrong index", __LINE__, idx)	
		return FALSE;
	}
	
	/* find aged blocked in cache */
	pblock = point_block_to_replace();

	if (NULL == pblock)
		return FALSE;

	p = pblock->p_arr;

	// support for uncompressed db later
	//if (Uncompressed) {

	//	index_t block = egtb_block_getnumber (wm, wk, bm, bk, wmrank, bmrank, color, idx);
	//	index_t n     = egtb_block_getsize   (key, idx);

	//	ok =	   egtb_file_beready (key)
	//			&& egtb_block_park   (key, block)
	//			&& egtb_block_read   (key, n, Buffer_packed)
	//			&& egtb_block_unpack (side, n, Buffer_packed, p);	

	//	FOLLOW_LULU("preload_cache", __LINE__, ok)

	//	assert (decoding_scheme() == 0 && GTB_scheme == 0);	

	//	if (ok) { Bytes_read = Bytes_read + (uint64_t) n; }

	//} else {

        index_t block = 0;
		index_t n = 0;
		index_t z = 0;
		
		ok =	   egtb_file_beready (key);

		FOLLOW_LULU("preload_cache", __LINE__, ok)

		if (ok) {
			block = egtb_block_getnumber (wm, wk, bm, bk, wmrank, bmrank, color, idx);
			n     = egtb_block_getsize   (wm, wk, bm, bk, wmrank, bmrank, color, idx);
			z     = egtb_block_getsize_zipped (wm, wk, bm, bk, wmrank, bmrank, color, block);				
		}

		ok =	   ok
				&& egtb_block_park   (key, wm, wk, bm, bk, wmrank, bmrank, color, block);
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		ok =	   ok	
				&& egtb_block_read   (key, z, Buffer_zipped);
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		ok =	   ok		
				&& egtb_block_decode (key, z, Buffer_zipped, n, Buffer_packed);
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		// this unpacking possibly special for chess, not checkers
		//ok =	   ok		 
		//		&& egtb_block_unpack (side, n, Buffer_packed, p);	
		//FOLLOW_LULU("preload_cache", __LINE__, ok)

		if (ok) { Bytes_read = Bytes_read + (uint64_t) z; }
	// }

	if (ok) {

		index_t 		offset;
		index_t			remainder;
		split_index (dtm_cache.entries_per_block, idx, &offset, &remainder); 

		pblock->key    = key;
		pblock->fileoffset = pSubDb->offset;
		pblock->offset = offset;
	} else {
		/* make it unusable */
		pblock->key    = -1;
		pblock->fileoffset = gtbNOINDEX;
		pblock->offset = gtbNOINDEX;
	}

	FOLLOW_LU("preload_cache?", ok)

	return ok;		
}


// gets DTM value
mySHARED bool_t
get_dtm (tbkey_t key, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t idx, dtm_t *out, bool_t probe_hard_flag)
{
	bool_t found;

	if (probe_hard_flag) {
		dtm_cache.hard++;
	} else {
		dtm_cache.soft++;
	}

	struct subendgame* pSubDb = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);

	if (get_dtm_from_cache (key, pSubDb->offset, idx, out)) {
		dtm_cache.hits++;
		found = TRUE;
	} else if (probe_hard_flag) {
		dtm_cache.hardmisses++;
		found = preload_cache (key, wm, wk, bm, bk, wmrank, bmrank, color, idx) &&
			get_dtm_from_cache (key, pSubDb->offset, idx, out);

		if (found) {
			Drive.hits++;			
		} else {
			Drive.miss++;					
		}		
	} else {
		dtm_cache.softmisses++;
		found = FALSE;
	}
	return found;
}


// support for uncompressed databases possibly later
//static bool_t
//egtb_filepeek (tbkey_t key, unsigned side, index_t idx, dtm_t *out_dtm)
//{
//	FILE *finp;
//
//#define USE_FD
//
//	#if !defined(USE_FD)
//	char buf[1024];
//	char *filename = buf;
//	#endif
//
//	bool_t ok;
//    dtm_t x=0;
//	index_t maxindex  = egkey[key].maxindex;
//
//
//
//	assert (Uncompressed);
//	assert (side == WH || side == BL);
//	assert (out_dtm != NULL);
//	assert (idx >= 0);
//	assert (key < MAX_EGKEYS);
//
//		
//	#if defined(USE_FD)
//		if (NULL == (finp = egkey[key].fd) ) {
//			if (NULL == (finp = fd_openit (key))) {
//				return FALSE;
//			}	
//		}
//	#else
//		sprintf (buf, "%s.gtb", egkey[key].str);
//		if (NULL == (finp = fopen (filename, "rb"))) {
//			return FALSE;
//		}	
//	#endif
//
//	ok = fpark_entry_packed (finp, side, maxindex, idx);
//	ok = ok && fread_entry_packed (finp, side, &x);
//
//	if (ok) {
//		*out_dtm = x;		
//	} else
//		*out_dtm = iFORBID;
//
//	#if !defined(USE_FD)
//	fclose (finp);
//	#endif
//
//	return ok;
//}


// gets DTM out of cache or disk
extern bool_t
egtb_get_dtm (tbkey_t k, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, bool_t probe_hard_flag, dtm_t *dtm)
{
	index_t idx;

	FOLLOW_label("egtb_get_dtm --> starts")

	bool_t success;

	/* 
	|		LOCK 
	*-------------------------------*/

#ifdef THREADING
	mythread_mutex_lock (&Egtb_lock);	
#endif

	if (dtm_cache_is_on()) {

		success = get_dtm(k, wm, wk, bm, bk, wmrank, bmrank, color, idx, dtm, probe_hard_flag);

		FOLLOW_LU("get_dtm (succ)",success)
		FOLLOW_LU("get_dtm (dtm )",*dtm)

		#if defined(DEBUG)
		if (Uncompressed) {
			dtm_t 	dtm_temp;
			bool_t 	ok;
			bool_t 	success2;

			assert (decoding_scheme() == 0 && GTB_scheme == 0);

			success2 = egtb_filepeek (k, stm, idx, &dtm_temp);
			ok =  (success == success2) && (!success || *dtm == dtm_temp);
			if (!ok) {
				printf ("\nERROR\nsuccess1=%d sucess2=%d\n"
						"k=%d stm=%u idx=%d dtm_peek=%d dtm_cache=%d\n", 
						success, success2, k, stm, idx, dtm_temp, *dtm);
				fatal_error();
			}
		}
		#endif

	} 
	else 
	{	
		// support for uncompressed db possibly later
		//assert(Uncompressed);		
		//if (probe_hard_flag && Uncompressed)
		//	success = egtb_filepeek (k, stm, idx, dtm);
		//else
		//	success = FALSE;
	}

#ifdef THREADING
	/*------------------------------*\ 
	|		UNLOCK 
	*/

	mythread_mutex_unlock (&Egtb_lock);	
#endif			

	if (success) {
		return TRUE;
	} else {
		*dtm = iUNKNOWN;
		return FALSE;
	}
} 


mySHARED dtm_t
inv_dtm (dtm_t x)
{
	unsigned mat;
	assert ( (x & iUNKNBIT) == 0);

	if (x == iDRAW || x == iFORBID)
		return x;
	
	mat = (unsigned)x & 3u;
	if (mat == iWMATE)
		mat = iBMATE;
	else
		mat = iWMATE;

	x = (dtm_t) (((unsigned)x & ~3u) | mat);

	return x;
}


// loads and decompresses WDL block out of DTM block
extern bool_t
wdl_preload_cache (tbkey_t key, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t idx)
/* output to the least used block of the cache */
{
	dtm_block_t		*dtm_block;
	wdl_block_t 	*to_modify;
	bool_t 			ok;
	struct subendgame* pSubDb = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);

	FOLLOW_label("wdl preload_cache starts");

	// check max index
	if (idx >= pSubDb->maxindex) 
	{
		FOLLOW_LULU("Wrong index", __LINE__, idx)	
		return FALSE;
	}

	/* find fresh block in dtm cache */
	dtm_block = dtm_cache_pointblock (key, pSubDb->offset , idx); 

	/* find aged blocked in wdl cache */
	to_modify = wdl_point_block_to_replace ();

	ok = !(NULL == dtm_block || NULL == to_modify);

	if (!ok)
		return FALSE;
	
	/* transform and move a block */
	dtm_block_2_wdl_block(dtm_block, to_modify, dtm_cache.entries_per_block);	

	if (ok) {
		index_t 		offset;
		index_t			remainder;
		split_index (wdl_cache.entries_per_block, idx, &offset, &remainder); 

		to_modify->key    = key;
		to_modify->fileoffset = pSubDb->offset;
		to_modify->offset = offset;
	} else {
		/* make it unusable */
		to_modify->key    = -1;
		to_modify->fileoffset = gtbNOINDEX;
		to_modify->offset = gtbNOINDEX;
	}

	FOLLOW_LU("wdl preload_cache?", ok)

	return ok;		
}

// returns oppsite of WDL
extern unsigned int
inv_wdl(unsigned w)
{
	unsigned r = tb_UNKNOWN;
	switch (w) {
		case tb_DRAW:    r = tb_DRAW;    break;
		case tb_WMATE:   r = tb_BMATE;   break;
		case tb_BMATE:   r = tb_WMATE;   break;
		case tb_FORBID:  r = tb_FORBID;  break;
		case tb_UNKNOWN: r = tb_UNKNOWN; break;
		default:         r = tb_UNKNOWN; break;
	}
	return r;
}

// returns WDL value out of cache or file
extern bool_t
get_WDL (tbkey_t key, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, index_t idx, unsigned int *info_out, bool_t probe_hard_flag)
{
	dtm_t dtm;
	bool_t found;
	struct subendgame* pSubDb = &(egSubDb[wm][wk][bm][bk][wmrank][bmrank][color]);

	found = get_WDL_from_cache (key, pSubDb->offset, idx, info_out);

	if (found) {
		wdl_cache.hits++;
	} else {
		/* may probe soft */
		found = get_dtm (key, wm, wk, bm, bk, wmrank, bmrank, color, idx, &dtm, probe_hard_flag);
		if (found) {
			*info_out = dtm2WDL(dtm);			
			/* move cache info from dtm_cache to WDL_cache */
			if (wdl_cache_is_on())
				wdl_preload_cache (key, wm, wk, bm, bk, wmrank, bmrank, color, idx);
		} 
	}

	if (probe_hard_flag) {
		wdl_cache.hard++;
		if (!found) {
			wdl_cache.hardmisses++;
		}
	} else {
		wdl_cache.soft++;
		if (!found) {
			wdl_cache.softmisses++;
		}
	}

	return found;
}


// gets the WDL value
extern bool_t
egtb_get_wdl (tbkey_t k, int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, bool_t probe_hard_flag, unsigned int *wdl)
{
	index_t idx;

	FOLLOW_label("egtb_get_wdl --> starts")

	bool_t success;

	/* 
	|		LOCK 
	*-------------------------------*/
#ifdef THREADING
	mythread_mutex_lock (&Egtb_lock);	
#endif

	success = get_WDL (k, wm, wk, bm, bk, wmrank, bmrank, color, idx, wdl, probe_hard_flag);
	FOLLOW_LU("get_wld (succ)",success)
	FOLLOW_LU("get_wld (wdl )",*wdl)

	// support for uncompressed databases maybe later
	/* this may not be needed */
	//if (!success) {		
	//	
	//	dtm_t dtm;
	//	unsigned res, ply;
	//	if (probe_hard_flag && Uncompressed) {
	//		assert(Uncompressed);
	//		success = egtb_filepeek (k, stm, idx, &dtm);
	//		unpackdist (dtm, &res, &ply);			
	//		*wdl = res;		
	//	}
	//	else
	//		success = FALSE;
	//}

#ifdef THREADING
	mythread_mutex_unlock (&Egtb_lock);	
#endif
	/*------------------------------*\ 
	|		UNLOCK 
	*/
} 

}
